// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#include "runtime_types.h"
#include "mem_alloc.h"
#include "string.h"
#include "panic.h"
#include "assert.h"

struct __go_open_array
__go_string_to_byte_array(struct __go_string str)
{
	unsigned char *data;
	struct __go_open_array ret;

	data = (unsigned char *)runtime_mallocgc(str.__length, FlagNoPointers, 1, 0);
	__builtin_memcpy(data, str.__data, str.__length);
	ret.__values = (void *)data;
	ret.__count = str.__length;
	ret.__capacity = str.__length;
	return ret;
}

struct __go_string
__go_byte_array_to_string(const void* p, int len)
{
	const unsigned char *bytes;
	unsigned char *retdata;
	struct __go_string ret;

	bytes = (const unsigned char *)p;
	retdata = runtime_mallocgc(len, FlagNoPointers, 1, 0);
	__builtin_memcpy(retdata, bytes, len);
	ret.__data = retdata;
	ret.__length = len;
	return ret;
}

struct __go_open_array
__go_string_to_int_array(struct __go_string str)
{
	uintptr c;
	const unsigned char *p;
	const unsigned char *pend;
	uint32 *data;
	uint32 *pd;
	struct __go_open_array ret;

	c = 0;
	p = str.__data;
	pend = p + str.__length;
	while (p < pend) {
		int rune;

		++c;
		p += __go_get_rune(p, pend - p, &rune);
	}

	data = (uint32 *)runtime_mallocgc(c * sizeof(uint32), FlagNoPointers, 1, 0);
	p = str.__data;
	pd = data;
	while (p < pend) {
		int rune;

		p += __go_get_rune(p, pend - p, &rune);
		*pd++ = rune;
	}

	ret.__values = (void *)data;
	ret.__count = c;
	ret.__capacity = c;
	return ret;
}

struct __go_string
__go_int_array_to_string(const void* p, int len)
{
	const int *ints;
	int slen;
	int i;
	unsigned char *retdata;
	struct __go_string ret;
	unsigned char *s;

	ints = (const int *)p;

	slen = 0;
	for (i = 0; i < len; i++) {
		int v;

		v = ints[i];

		if (v > 0x10ffff)
			v = 0xfffd;

		if (v <= 0x7f)
			slen += 1;
		else if (v <= 0x7ff)
			slen += 2;
		else if (v <= 0xffff)
			slen += 3;
		else
			slen += 4;
	}

	retdata = runtime_mallocgc(slen, FlagNoPointers, 1, 0);
	ret.__data = retdata;
	ret.__length = slen;

	s = retdata;
	for (i = 0; i < len; i++) {
		int v;

		v = ints[i];

		// If V is out of range for UTF-8, substitute the replacement character.
		if (v > 0x10ffff)
			v = 0xfffd;

		if (v <= 0x7f) {
			*s++ = v;
		} else if (v <= 0x7ff) {
			*s++ = 0xc0 | ((v >> 6) & 0x1f);
			*s++ = 0x80 | (v & 0x3f);
		} else if (v <= 0xffff) {
			*s++ = 0xe0 | ((v >> 12) & 0xf);
			*s++ = 0x80 | ((v >> 6) & 0x3f);
			*s++ = 0x80 | (v & 0x3f);
		} else {
			*s++ = 0xf0 | ((v >> 18) & 0x7);
			*s++ = 0x80 | ((v >> 12) & 0x3f);
			*s++ = 0x80 | ((v >> 6) & 0x3f);
			*s++ = 0x80 | (v & 0x3f);
		}
	}

	__go_assert(s - retdata == slen);
	return ret;
}

struct __go_string
__go_int_to_string(int v)
{
	char buf[4];
	int len;
	unsigned char *retdata;
	struct __go_string ret;

	if (v <= 0x7f) {
		buf[0] = v;
		len = 1;
	} else if (v <= 0x7ff) {
		buf[0] = 0xc0 + (v >> 6);
		buf[1] = 0x80 + (v & 0x3f);
		len = 2;
	} else {
		// If the value is out of range for UTF-8, turn it into the "replacement character".
		if (v > 0x10ffff)
			v = 0xfffd;

		if (v <= 0xffff) {
			buf[0] = 0xe0 + (v >> 12);
			buf[1] = 0x80 + ((v >> 6) & 0x3f);
			buf[2] = 0x80 + (v & 0x3f);
			len = 3;
		} else {
			buf[0] = 0xf0 + (v >> 18);
			buf[1] = 0x80 + ((v >> 12) & 0x3f);
			buf[2] = 0x80 + ((v >> 6) & 0x3f);
			buf[3] = 0x80 + (v & 0x3f);
			len = 4;
		}
	}

	retdata = runtime_mallocgc (len, FlagNoPointers, 1, 0);
	__builtin_memcpy (retdata, buf, len);
	ret.__data = retdata;
	ret.__length = len;

	return ret;
}

struct __go_string
__go_string_plus(struct __go_string s1, struct __go_string s2)
{
	int len;
	unsigned char *retdata;
	struct __go_string ret;

	if (s1.__length == 0)
		return s2;
	else if (s2.__length == 0)
		return s1;

	len = s1.__length + s2.__length;
	retdata = runtime_mallocgc(len, FlagNoPointers, 1, 0);
	__builtin_memcpy(retdata, s1.__data, s1.__length);
	__builtin_memcpy(retdata + s1.__length, s2.__data, s2.__length);
	ret.__data = retdata;
	ret.__length = len;
	return ret;
}

int
__go_strcmp(struct __go_string s1, struct __go_string s2)
{
	int i;

	i = __builtin_memcmp(s1.__data, s2.__data, (s1.__length < s2.__length ? s1.__length : s2.__length));
	if (i != 0) {
		return i;
	}
	
	if (s1.__length < s2.__length) {
		return -1;
	}
	if (s1.__length > s2.__length) {
		return 1;
	}
	return 0;
}

struct __go_string
__go_string_slice(struct __go_string s, int start, int end)
{
	int len;
	struct __go_string ret;

	len = s.__length;
	if (end == -1)
		end = len;
	if (start > len || end < start || end > len)
		runtime_panicstring("string index out of bounds");
	ret.__data = s.__data + start;
	ret.__length = end - start;
	return ret;
}


// Get a character from the UTF-8 string STR, of length LEN.  Store
// the Unicode character, if any, in *RUNE.  Return the number of
// characters used from STR.
int
__go_get_rune(const unsigned char *str, uintptr len, int *rune)
{
	int c, c1, c2, c3;

	// Default to the "replacement character".
	*rune = 0xfffd;

	if (len <= 0)
		return 1;

	c = *str;
	if (c <= 0x7f) {
		*rune = c;
		return 1;
	}

	if (len <= 1)
		return 1;

	c1 = str[1];
	if ((c & 0xe0) == 0xc0 && (c1 & 0xc0) == 0x80) {
		*rune = (((c & 0x1f) << 6) + (c1 & 0x3f));
		return 2;
	}

	if (len <= 2)
		return 1;

	c2 = str[2];
	if ((c & 0xf0) == 0xe0 && (c1 & 0xc0) == 0x80 && (c2 & 0xc0) == 0x80) {
		*rune = (((c & 0xf) << 12) + ((c1 & 0x3f) << 6) + (c2 & 0x3f));
		return 3;
	}

	if (len <= 3)
		return 1;

	c3 = str[3];
	if ((c & 0xf8) == 0xf0 && (c1 & 0xc0) == 0x80 && (c2 & 0xc0) == 0x80 && (c3 & 0xc0) == 0x80) {
		*rune = (((c & 0x7) << 18) + ((c1 & 0x3f) << 12) + ((c2 & 0x3f) << 6) + (c3 & 0x3f));
		return 4;
	}

	// Invalid encoding.  Return 1 so that we advance.
	return 1;
}
